package mymodbus

import (
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/goburrow/modbus"
	"iot/common"
	"iot/common/sys"
	"iot/plat/device"
	"math"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"unicode"
)

type CModbusObj struct {
	common.SModbusPoints
	common.SArgumentInfo
}

type CModbusUnit struct {
	LastTimeMark time.Time
	Type         common.EModbusRegisterType
	Interval     int
	Address      uint16
	Count        uint16
	Points       []CModbusObj
}

type CModbus struct {
	cfg       common.SCommunicationConfig
	dev       *device.CDevice
	units     []CModbusUnit
	slaveId   uint16
	client    modbus.Client        // modbus 客户端
	handler   modbus.ClientHandler // modbus 连接句柄
	mu        sync.RWMutex         // 用于线程安全
	connected atomic.Bool          // 连接状态标志
	isRunning atomic.Bool

	ticker   *time.Ticker   // 定时器
	stopChan chan struct{}  // 停止信号通道
	wg       sync.WaitGroup // 等待组用于优雅停止
}

type ModbusTcpConfig struct {
	common.TCPConfig
	SlaveId uint16 `json:"slave_id"`
}

type ModbusRtuConfig struct {
	common.SerialConfig
	SlaveId uint16 `json:"slave_id"`
}

func (mb *CModbus) Init(dev *device.CDevice, cfg common.SCommunicationConfig, mapidx string) error {
	mb.mu.Lock()
	defer mb.mu.Unlock()

	mb.isRunning.Store(false)
	mb.connected.Store(false)

	mb.dev = dev
	mb.cfg = cfg
	if mb.dev == nil {
		return fmt.Errorf("modbus device not initialized")
	}

	if err := mb.ParseMap(mapidx); err != nil {
		return err
	}

	switch mb.cfg.ConfigType {
	case "serial": // RTU 模式
		var conf ModbusRtuConfig
		if err := json.Unmarshal([]byte(cfg.ConfigJson), &conf); err != nil {
			return fmt.Errorf("modbus serial config unmarshal error: %v", err)
		}

		handler := modbus.NewRTUClientHandler(conf.Port)
		handler.BaudRate = conf.BaudRate
		handler.DataBits = conf.DataBits
		handler.StopBits = conf.StopBits
		handler.Parity = string(conf.Parity)
		handler.SlaveId = byte(conf.SlaveId)
		handler.Timeout = time.Duration(conf.Timeout) * time.Second

		mb.handler = handler
		mb.client = modbus.NewClient(handler)
		if mb.client != nil {
			return fmt.Errorf("modbus rtu client initialized")
		}
	case "tcp": // TCP 模式
		var conf ModbusTcpConfig
		if err := json.Unmarshal([]byte(mb.cfg.ConfigJson), &conf); err != nil {
			return fmt.Errorf("TCP config unmarshal error: %v", err)
		}

		handler := modbus.NewTCPClientHandler(conf.IP + ":" + strconv.Itoa(conf.Port))
		handler.SlaveId = byte(0x01)
		handler.Timeout = time.Duration(conf.Timeout) * time.Second

		mb.handler = handler
		mb.client = modbus.NewClient(handler)
		if mb.client == nil {
			return fmt.Errorf("modbus tcp client initialized")
		}
	default:
		return fmt.Errorf("unsupported config type: %s", cfg.ConfigType)
	}

	return nil
}

func (mb *CModbus) Run() error {
	if mb.isRunning.Load() {
		return fmt.Errorf("modbus is already running")
	}

	// 确保已连接
	if !mb.connected.Load() {
		if err := mb.Connect(); err != nil {
			return fmt.Errorf("connect failed: %w", err)
		}
	}

	devRtInfo := mb.dev.RtInfo()
	devRtInfo.StartupTime = time.Now()
	devRtInfo.IsOnline = true
	devRtInfo.AlarmStatus = common.EAlarmStatusNormal
	mb.dev.SetRtInfo(devRtInfo)

	// 创建定时器和停止通道
	mb.ticker = time.NewTicker(100 * time.Millisecond)
	mb.stopChan = make(chan struct{})

	// 启动数据采集协程
	mb.wg.Add(1)
	go mb.dataCollectionLoop()

	mb.isRunning.Store(true)
	mb.connected.Store(true)

	return nil
}

func (mb *CModbus) Stop() error {
	if !mb.isRunning.Load() {
		return nil
	}

	// 发送停止信号
	close(mb.stopChan)

	// 等待采集协程结束
	mb.wg.Wait()

	// 停止定时器
	if mb.ticker != nil {
		mb.ticker.Stop()
		mb.ticker = nil
	}

	mb.isRunning.Store(false)
	return nil
}

func (mb *CModbus) Connect() error {
	if mb.connected.Load() {
		return nil
	}

	mb.mu.RLock()
	defer mb.mu.RUnlock()
	if mb.handler == nil {
		return fmt.Errorf("modbus handler not initialized")
	}

	mb.connected.Store(true)
	return nil
}

func (mb *CModbus) Disconnect() error {
	mb.connected.Store(false)
	return nil
}

func (mb *CModbus) dataCollectionLoop() {
	defer mb.wg.Done()

	for {
		select {
		case <-mb.ticker.C:
			// 执行数据采集
			mb.collectData()

		case <-mb.stopChan:
			// 收到停止信号
			return
		}
	}
}

// 执行实际的数据采集
func (mb *CModbus) collectData() {
	for i, unit := range mb.units {
		elapsed := time.Since(unit.LastTimeMark).Milliseconds()
		if elapsed < int64(unit.Interval) {
			continue
		}

		var rawBytes []byte
		var err error
		var isCoilType bool

		switch unit.Type {
		case common.EModbusRegisterCoil, common.EModbusRegisterDiscreteInput:
			isCoilType = true
			mb.mu.RLock()
			if unit.Type == common.EModbusRegisterCoil {
				rawBytes, err = mb.client.ReadCoils(unit.Address, unit.Count)
			} else {
				rawBytes, err = mb.client.ReadDiscreteInputs(unit.Address, unit.Count)
			}
			mb.mu.RUnlock()
		case common.EModbusRegisterInputRegister, common.EModbusRegisterHolding:
			isCoilType = false
			mb.mu.RLock()
			if unit.Type == common.EModbusRegisterInputRegister {
				rawBytes, err = mb.client.ReadInputRegisters(unit.Address, unit.Count)
			} else {
				rawBytes, err = mb.client.ReadHoldingRegisters(unit.Address, unit.Count)
			}
			mb.mu.RUnlock()
		default:
			continue
		}

		if err != nil {
			continue
		}

		rtvals := make([]device.SRtValue, len(unit.Points))
		for i, _ := range unit.Points {
			rtvals[i].Value = ""
			rtvals[i].TimeMark = time.Now()
			rtvals[i].Source = common.EDataSourceAcquisition
			rtvals[i].Quality = common.EDataQualityBad
		}

		for i := range unit.Points {
			rtvals[i].ArgIndex = unit.Points[i].PIndex
			if isCoilType {
				point_index := i / 8
				point_offest := i % 8

				rtvals[i].Quality = common.EDataQualityGood

				if uint8((rawBytes[point_index]>>point_offest)&0x01) == 0 {
					if unit.Points[i].ValueType == common.EArgTypeBool {
						rtvals[i].Value = "false"
					} else {
						rtvals[i].Value = "0"
					}
				} else {
					if unit.Points[i].ValueType == common.EArgTypeBool {
						rtvals[i].Value = "true"
					} else {
						rtvals[i].Value = "1"
					}
				}
			} else {
				startIdx := i * 2
				if startIdx+2 > len(rawBytes) {
					continue
				}

				valBytes := rawBytes[startIdx : startIdx+2]
				if valstr, err := mb.decodeValue(&unit.Points[i], valBytes); err == nil {
					rtvals[i].Value = valstr
					rtvals[i].Quality = common.EDataQualityGood
				}
			}
			if mb.dev.RtArg(rtvals[i].ArgIndex).Value != rtvals[i].Value {
				sys.Bus().Publish(common.MonitorPointChange, rtvals[i])
			}
		}

		// 更新最后处理时间
		mb.units[i].LastTimeMark = time.Now()
	}
}

func (mb *CModbus) decodeValue(point *CModbusObj, rawBytes []byte) (string, error) {
	// 根据参数类型进行解码
	switch point.ValueType {
	case common.EArgTypeInt:
		return mb.decodeIntValue(point, rawBytes)
	case common.EArgTypeFloat:
		return mb.decodeFloatValue(point, rawBytes)
	case common.EArgTypeString:
		return mb.decodeStringValue(point, rawBytes)
	case common.EArgTypeBool:
		return mb.decodeBoolValue(point, rawBytes)
	case common.EArgTypeBytes:
		return mb.decodeBytesValue(rawBytes)
	default:
		return "", fmt.Errorf("unsupported value type: %d", point.ValueType)
	}
}

func (mb *CModbus) decodeIntValue(point *CModbusObj, rawBytes []byte) (string, error) {
	var result uint64
	byteLen := len(rawBytes)

	// 根据字节序解码
	switch point.ByteOrder {
	case common.EByteOrderLittleEndian:
		for i, b := range rawBytes {
			result |= uint64(b) << (8 * (byteLen - 1 - i))
		}
	case common.EByteOrderBigEndian:
		for i, b := range rawBytes {
			result |= uint64(b) << (8 * i)
		}
	default:
		return "", fmt.Errorf("invalid byte order: %d", point.ByteOrder)
	}

	// 应用偏移量
	value := int64(result) + int64(point.Offset)
	return strconv.FormatInt(value, 10), nil
}

func (mb *CModbus) decodeFloatValue(point *CModbusObj, rawBytes []byte) (string, error) {
	switch len(rawBytes) {
	case 4:
		var bits uint32
		if point.ByteOrder == common.EByteOrderBigEndian {
			bits = binary.BigEndian.Uint32(rawBytes)
		} else {
			bits = binary.LittleEndian.Uint32(rawBytes)
		}
		floatValue := float64(math.Float32frombits(bits)) + float64(point.Offset)
		return strconv.FormatFloat(floatValue, 'f', -1, 32), nil

	case 8:
		var bits uint64
		if point.ByteOrder == common.EByteOrderBigEndian {
			bits = binary.BigEndian.Uint64(rawBytes)
		} else {
			bits = binary.LittleEndian.Uint64(rawBytes)
		}
		floatValue := math.Float64frombits(bits) + float64(point.Offset)
		return strconv.FormatFloat(floatValue, 'f', -1, 64), nil

	default:
		return "", fmt.Errorf("invalid length %d for float conversion", len(rawBytes))
	}
}

func (mb *CModbus) decodeStringValue(point *CModbusObj, rawBytes []byte) (string, error) {
	// 移除尾部空白字符
	str := strings.TrimRight(string(rawBytes), "\x00")

	// 简单清理非打印字符（可根据需要扩展）
	cleanStr := strings.Map(func(r rune) rune {
		if unicode.IsPrint(r) {
			return r
		}
		return -1
	}, str)

	return cleanStr, nil
}

func (mb *CModbus) decodeBoolValue(point *CModbusObj, rawBytes []byte) (string, error) {
	if len(rawBytes) == 0 {
		return "false", nil
	}

	// 检查第一个字节的最低有效位
	if rawBytes[0]&0x01 != 0 {
		return "true", nil
	}
	return "false", nil
}

func (mb *CModbus) decodeBytesValue(rawBytes []byte) (string, error) {
	// 转换为十六进制字符串表示
	hexStr := hex.EncodeToString(rawBytes)

	// 每两个字符插入空格以提高可读性
	var builder strings.Builder
	for i := 0; i < len(hexStr); i += 2 {
		if i > 0 {
			builder.WriteByte(' ')
		}
		end := i + 2
		if end > len(hexStr) {
			end = len(hexStr)
		}
		builder.WriteString(hexStr[i:end])
	}
	return builder.String(), nil
}

func (mb *CModbus) ParseMap(mapidx string) error {
	db := sys.DbIns()
	meatPoints := make([]common.SModbusPoints, 0)
	if err := db.Where("protocol_index = ?", mapidx).Find(&meatPoints).Error; err != nil {
		return err
	}

	// 如果没有点位数据，直接返回
	if len(meatPoints) == 0 {
		return nil
	}

	addrGroups := make(map[string][]common.SModbusPoints)
	for _, point := range meatPoints {
		// 生成分组键：SlaveID + 类型 + 地址
		key := fmt.Sprintf("%d-%d-%d", point.SlaveId, point.Type, point.Address)
		addrGroups[key] = append(addrGroups[key], point)
	}

	// 将分组转换为切片，并按SlaveId > 类型 > 地址顺序排序
	groupedPoints := make([][]common.SModbusPoints, 0, len(addrGroups))
	for _, group := range addrGroups {
		groupedPoints = append(groupedPoints, group)
	}

	sort.Slice(groupedPoints, func(i, j int) bool {
		a := groupedPoints[i][0]
		b := groupedPoints[j][0]
		if a.SlaveId != b.SlaveId {
			return a.SlaveId < b.SlaveId
		}
		if a.Type != b.Type {
			return a.Type < b.Type
		}
		return a.Address < b.Address
	})

	// 合并连续分组：将地址连续且类型相同的分组合并
	finalGroups := make([][]common.SModbusPoints, 0)
	if len(groupedPoints) == 0 {
		return nil
	}

	currentGroup := groupedPoints[0]
	for i := 1; i < len(groupedPoints); i++ {
		prev := groupedPoints[i-1][0]
		curr := groupedPoints[i][0]

		// 检查是否可合并：相同从机、类型连续地址
		sameDevice := prev.SlaveId == curr.SlaveId
		sameType := prev.Type == curr.Type
		addressContiguous := curr.Address == prev.Address+1

		if sameDevice && sameType && addressContiguous {
			// 合并分组
			currentGroup = append(currentGroup, groupedPoints[i]...)
		} else {
			// 新分组
			finalGroups = append(finalGroups, currentGroup)
			currentGroup = groupedPoints[i]
		}
	}
	finalGroups = append(finalGroups, currentGroup) // 添加最后一个分组

	mb.units = make([]CModbusUnit, len(finalGroups))

	// 打印分组结果（可根据需要替换为实际处理逻辑）
	for i, group := range finalGroups {
		mbgroup := CModbusUnit{
			Points: make([]CModbusObj, len(group)),
		}
		groupInteval := 1000000
		minaddrs := group[0].Address
		maxaddrs := group[0].Address
		mbgroup.Type = group[0].Type
		for j, point := range group {
			if point.Interval < groupInteval {
				groupInteval = point.Interval
			}

			if point.Address < minaddrs {
				minaddrs = point.Address
			}

			if point.Address > maxaddrs {
				maxaddrs = point.Address
			}

			mbgroup.Points[j] = CModbusObj{
				SModbusPoints: point,
				SArgumentInfo: mb.dev.Argument(point.AgrIndex).SArgumentInfo,
			}
		}

		if groupInteval == 1000000 {
			groupInteval = 5000
		}

		mbgroup.Interval = groupInteval
		mbgroup.Address = minaddrs
		mbgroup.Count = maxaddrs - minaddrs + 1
		mbgroup.LastTimeMark = time.Now()
		mb.units[i] = mbgroup
		//fmt.Printf("========== Group %d ==========\n", i+1)
		//fmt.Printf("SlaveID: %d | Type: %d | StartAddr: %d | Points: %d\n",
		//	group[0].SlaveId, group[0].Type, group[0].Address, len(group))
		//
		//// 打印组内点位详情
		//for j, point := range group {
		//	addrType := "Register"
		//	if point.Type == common.EModbusRegisterCoil || point.Type == common.EModbusRegisterDiscreteInput {
		//		addrType = "Bit"
		//	}
		//
		//	fmt.Printf("  Point %d: Addr=%d (%s) ", j+1, point.Address, addrType)
		//	if point.Type == common.EModbusRegisterCoil {
		//		fmt.Printf("| Offset=%d", point.Offset)
		//	}
		//	fmt.Println()
		//}
	}
	return nil
}
